﻿
namespace Patterns4Net.Core.Patterns
{
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using Mono.Cecil;
    using CecilExtensions;

    public class Visitor : PatternBase
    {        
        private IEnumerable<TypeDefinition> elements = null;

        public Visitor(TypeDefinition visitorType, TypeReference elementsRoot, bool isReflective)
        {
            this.IsReflective = isReflective;
            this.VisitorType = visitorType;
            this.VisitableElementsRoot = elementsRoot;
        }

        public bool IsReflective { get; set; }

        public TypeDefinition VisitorType { get; private set; }

        public TypeReference VisitableElementsRoot { get; private set; }

        public IEnumerable<TypeDefinition> VisitableElements
        {
            get
            {
                Contract.Ensures(Contract.Result<IEnumerable<TypeDefinition>>() != null);
                Contract.Ensures(Contract.ForAll(Contract.Result<IEnumerable<TypeDefinition>>(), x => x != null));
                if (this.elements == null)
                {
                    this.elements = this.CalculateElements();
                }

                return this.elements;
            }
        }

        private IEnumerable<TypeDefinition> CalculateElements()
        {
            var result = new List<TypeDefinition>();
            var recursionStack = new Stack<TypeReference>();
            recursionStack.Push(this.VisitableElementsRoot);
            while (recursionStack.Count > 0)
            {
                var element = recursionStack.Pop().SafeResolve();
                result.Add(element);
                foreach (var child in element.GetChildTypes(element.Module))
                {
                    recursionStack.Push(child);
                }
            }

            return result;
        }
    }
}
